package com.mogujie.trade.db;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author by jiuru on 16/7/14.
*/
public class ReadWriteSplittingAdvice {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Map<Method, DataSourceType> cache = new ConcurrentHashMap<>();
@Around("@annotation(com.mogujie.trade.db.ReadWriteSplitting)")
public Object intercept(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Signature signature = proceedingJoinPoint.getSignature();
DataSourceType dataSourceType = null;
if (signature instanceof MethodSignature) {
// 若已经在事务中 则不做处理
if (RoutingDataSourceTransactionContext.getCurTransactionDataSource() != null) {
return proceedingJoinPoint.proceed();
}
// 若已经设置为Master 则忽略
if (ReadWriteSplittingContext.isMaster()) {
return proceedingJoinPoint.proceed();
}
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
dataSourceType = this.getDataSourceType(method);
} else {
// this may not happend.
throw new ReadWriteSplittingException("ReadWriteSplitting annotation should only used on method. ");
}
ReadWriteSplittingContext.set(dataSourceType);
logger.debug("{} {} using dataSourceOf {} ", proceedingJoinPoint.getTarget(),
proceedingJoinPoint.getSignature(), dataSourceType);
try {
return proceedingJoinPoint.proceed();
} finally {
ReadWriteSplittingContext.clear();
logger.debug("{} release dataSource of {}", proceedingJoinPoint.getTarget(), dataSourceType);
}
}
/**
* 获取方法对应的数据源类型
*
* @param method
* @return
*/
private DataSourceType getDataSourceType(Method method) {
DataSourceType dataSourceType = this.cache.get(method);
if (dataSourceType == null) {
synchronized (method) {
dataSourceType = this.cache.get(method);
if (dataSourceType == null) {
dataSourceType = this.determineDataSourceType(method);
this.cache.put(method, dataSourceType);
}
}
}
return dataSourceType;
}
private DataSourceType determineDataSourceType(Method method) {
DataSourceType dataSourceType = DataSourceType.slave;
ReadWriteSplitting readWriteSplitting = method.getAnnotation(ReadWriteSplitting.class);
if (readWriteSplitting != null) {
dataSourceType = readWriteSplitting.value();
dataSourceType = dataSourceType == null ? DataSourceType.master : dataSourceType;
} else {
// this will not happen
throw new InternalError("method must have the annotation of ReadWriteSplitting! ");
}
Transactional transcational = method.getAnnotation(Transactional.class);
if (transcational != null) {
throw new ReadWriteSplittingException("ReadWriteSplitting and Transactional can't be used on method "
+ method + " at the same time!");
}
return dataSourceType;
}
}